#ifndef _NAMED_SET_H_
#define _NAMED_SET_H_

#include "util/mmgr.h"
#include <memory>
#include "crc32.h"

#if defined(_PLATFORM_ANDROID) || defined(_PLATFORM_IOS)
	#include <ext/hash_map>
#else
	#include <hash_map>
#endif

#include <map>
#include <string>
#include <cassert>
#include <iostream>
#include "util/common_macros.h"

namespace num
{
/// provides a container using CRC32 to generate Hashes of names
template <class T> class NamedSet
{
public:

#if defined(_PLATFORM_WII)

	typedef Metrowerks::hash_map<unsigned long, T>		ElementMap;
	typedef std::pair<unsigned long, T>		ElementPair;
#elif defined(_PLATFORM_ANDROID) || defined(_PLATFORM_IOS)

	 typedef __gnu_cxx::hash_map<unsigned long, T>		ElementMap;
	 typedef __gnu_cxx::pair<unsigned long, T>		ElementPair;
#else

	typedef std::hash_map<unsigned long, T>		ElementMap;
	typedef std::pair<unsigned long, T>		ElementPair;
#endif //

	NamedSet(bool DeleteSet = true, bool IgnoreCase = true ) :
		m_DeleteSet(DeleteSet),
		m_IgnoreCase(IgnoreCase)
	{
		SET_OVERRUN(m_StartGuard);
		SET_OVERRUN(m_EndGuard);
	}
				
	virtual ~NamedSet( )
	{
		if (m_DeleteSet)
		{
			ClearSet();
		}
	}


	void			ClearSet()
	{
		if (m_DeleteSet)
		{
			m_Set.begin();
			ElementMap		m_Set;
			
			for (typename ElementMap::iterator It = m_Set.begin(); It != m_Set.end(); It++)
			{
				delete It->second;
			}
		}
		m_Set.clear();
	}

	/// add element to set
	bool			AddElement(const std::string& Name, T	Value)
	{
		if (!ElementExists(Name))
		{
			unsigned long	Key = GetKey(Name);
		    m_Set.insert(ElementPair(Key, Value));
			return true;
		}
		return false;
	}
	bool			AddElement(unsigned long Key, T	Value)
	{
		if (!ElementExists(Key))
		{
		    m_Set.insert(ElementPair(Key, Value));
			return true;
		}
		return false;
	}
	/// check if element exists
	bool			ElementExists(const std::string& Name) const
	{
		if (GetElement(Name) != NULL)
			return true;
		return false;
	}
	bool			ElementExists(unsigned int Key) const
	{
		if (GetElement(Key) != NULL)
			return true;
		return false;
	}	
	/// retrieve the element
	T				GetElement(const std::string& Name) const
	{
	CHECK_OVERRUN(m_StartGuard);
	CHECK_OVERRUN(m_EndGuard);
		const  char*	pStr  = Name.c_str();
		unsigned long	Key = GetKey(Name);
		typename ElementMap::const_iterator It = m_Set.find(Key);
	CHECK_OVERRUN(m_StartGuard);
	CHECK_OVERRUN(m_EndGuard);
		if (It != m_Set.end()) return It->second;
		return NULL;
	}
	/// retrieve the element by Key.. usefull for caching the key
	T				GetElement(unsigned long Key) const
	{
		typename ElementMap::const_iterator It = m_Set.find(Key);
		if (It != m_Set.end()) return It->second;
		return NULL;
	}

	/// retrieve the element
	T				GetElementByIndex(int Index) const
	{
		if (Index < 0 || Index >= (int)m_Set.size()) return NULL;	// invalid
		for (typename ElementMap::const_iterator It = m_Set.begin(); It != m_Set.end(); It++)
		{
			if (Index == 0) 			
				return It->second;
			Index--;
		}
		return NULL;
	}
	/// delete the element
	void			RemoveElement(unsigned long key, bool Delete = true)
	{
		typename ElementMap::iterator It = m_Set.find(key);
		if (It != m_Set.end())
		{
			if (Delete && m_DeleteSet)
			{
				delete It->second;
			}
			m_Set.erase(It);
		}
	}
	void			RemoveElement(const std::string& Name, bool Delete = true)
	{
		RemoveElement(GetKey(Name), Delete);
	}

	/// number of elements
	unsigned long	GetElementC() const
	{
		return (unsigned long)m_Set.size();
	}
	
	/// get the CRC32 number from the key.. usefull for caching the key
	unsigned long	GetKey(const std::string& Name) const
	{
		return CRC32::FastHash(Name, m_IgnoreCase);
	}

	typedef void	(*FindSetCB) (T, void* Data); // callback for effect param
	void			ForEach(FindSetCB pCB, void* Data)
	{
		for (typename ElementMap::iterator It = m_Set.begin(); It != m_Set.end(); It++)
		{
			pCB(It->second, Data);
		}
	}

	ADD_OVERRUN(m_StartGuard);
	ElementMap		m_Set;
	bool			m_DeleteSet;
	bool			m_IgnoreCase;
	ADD_OVERRUN(m_EndGuard);
};

} //namespace num
#endif  // _NAMED_SET_H_
